﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace System.Net.Battlefield3
{
    /// <summary>
    /// Provides methods for manipulating BF3's reserved slots.
    /// </summary>
    public sealed class ReservedSlotCollection
        : MarshalByRefObject, IEnumerable<string>
    {
        static Globalization.CultureInfo c = Globalization.CultureInfo.GetCultureInfo("en-US");

        const int MaxPageSize = 100;
        const int WordsPerItem = 1;
        RconClientBase Client;

        internal ReservedSlotCollection(RconClientBase client)
        {
            Client = client;
        }

        #region Helper methods
        Packet GetPacket(int offset)
        {
            return Client.InternalSendRequest("reservedSlotsList.list", offset.ToStringG());
        }

        //this will use yield and therefore be more efficient than receiving all of the items first
        IEnumerable<string> ParseItems()
        {
            int offset = 0;
            Packet p = GetPacket(offset);
            while (p.Success() && p.Words.Count > 1) //packet has items
            {
                offset += p.Words.Count - 1;

                for (int i = 1; i < p.Words.Count; i++)
                {
                    yield return p.Words[i];
                }

                p = GetPacket(offset);
            }
            yield break;
        }

        #endregion

        /// <summary>
        /// Gets or sets whether or not <see cref="Player">Players</see> not in the <see cref="ReservedSlotCollection"/>
        /// will be kicked when <see cref="Player">Players</see> in the <see cref="ReservedSlotCollection"/> try to join.
        /// </summary>
        /// <value>True if <see cref="Player">Players</see> not in the <see cref="ReservedSlotCollection"/>
        /// will be kicked when <see cref="Player">Players</see> in the <see cref="ReservedSlotCollection"/> try to join;
        /// null if the request failed.</value>
        public bool? AggressiveJoin
        {
            get
            {
                Packet p = Client.InternalSendRequest("reservedSlotsList.aggressiveJoin");

                return p.Success() ? bool.Parse(p.Words[1]) : (bool?)null;
            }

            set
            {
                Client.InternalSendRequest("reservedSlotsList.aggressiveJoin", (value ?? false).ToString(c));
            }
        }

        /// <summary>
        /// Adds a <see cref="Player"/> to the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <param name="player">The <see cref="Player"/> that will be added.</param>
        /// <returns>An <see cref="RconResult"/> containing success information on the request.</returns>
        /// <exception cref="NotLoggedOnException">The <see cref="RconClient"/> isn't logged on.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="player"/> must not be null.</exception>
        public RconResult Add(Player player)
        {
            if (!Client.IsLoggedOn) throw new NotLoggedOnException();

            if (player == null) throw new ArgumentNullException("player", "player must not be null.");

            return Client.InternalSendRequest("reservedSlotsList.add", player.Name).ToResult();
        }

        /// <summary>
        /// Removes a <see cref="Player"/> from the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <param name="player">The <see cref="Player"/> that will be removed.</param>
        /// <returns>An <see cref="RconResult"/> containing success information on the request.</returns>
        /// <exception cref="NotLoggedOnException">The <see cref="RconClient"/> isn't logged on.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="player"/> must not be null.</exception>
        public RconResult Remove(Player player)
        {
            if (!Client.IsLoggedOn) throw new NotLoggedOnException();

            if (player == null) throw new ArgumentNullException("player", "player must not be null.");

            return Client.InternalSendRequest("reservedSlotsList.remove", player.Name).ToResult();
        }

        /// <summary>
        /// Adds a player, by name, to the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <param name="player">The player that will be added.</param>
        /// <returns>An <see cref="RconResult"/> containing success information on the request.</returns>
        /// <exception cref="NotLoggedOnException">The <see cref="RconClient"/> isn't logged on.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="player"/> must not be null.</exception>
        public RconResult Add(string player)
        {
            if (!Client.IsLoggedOn) throw new NotLoggedOnException();

            if (player == null) throw new ArgumentNullException("player", "player must not be null.");

            return Client.InternalSendRequest("reservedSlotsList.add", player).ToResult();
        }

        /// <summary>
        /// Removes a player, by name, from the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <param name="player">The player that will be removed.</param>
        /// <returns>An <see cref="RconResult"/> containing success information on the request.</returns>
        /// <exception cref="NotLoggedOnException">The <see cref="RconClient"/> isn't logged on.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="player"/> must not be null.</exception>
        public RconResult Remove(string player)
        {
            if (!Client.IsLoggedOn) throw new NotLoggedOnException();

            if (player == null) throw new ArgumentNullException("player", "player must not be null.");

            return Client.InternalSendRequest("reservedSlotsList.remove", player).ToResult();
        }

        /// <summary>
        /// Clears all <see cref="Player">Players</see> from the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <returns>An <see cref="RconResult"/> containing success information on the request.</returns>
        /// <exception cref="NotLoggedOnException">The <see cref="RconClient"/> isn't logged on.</exception>
        public RconResult Clear()
        {
            if (!Client.IsLoggedOn) throw new NotLoggedOnException();

            return Client.InternalSendRequest("reservedSlotsList.clear").ToResult();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <returns>An <see cref="T:System.Collections.Generic.IEnumerator`1">IEnumerator(Ban)</see> that iterates through the <see cref="ReservedSlotCollection"/>.</returns>
        public IEnumerator<string> GetEnumerator()
        {
            return ParseItems().GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="ReservedSlotCollection"/>.
        /// </summary>
        /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that iterates through the <see cref="ReservedSlotCollection"/>.</returns>
        Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
        {
            return ParseItems().GetEnumerator();
        }
    }
}
